/**
 * Copyright (C) 2008 by The Regents of the University of California
 * Redistribution of this file is permitted under the terms of the GNU
 * Public License (GPL).
 *
 * @author Junghoo "John" Cho <cho AT cs.ucla.edu>
 * @date 3/24/2008
 */

#include <cstdio>
#include <iostream>
#include <fstream>
#include "Bruinbase.h"
#include "SqlEngine.h"
#include <fstream>
#include "BTreeIndex.h"


using namespace std;

// external functions and variables for load file and sql command parsing 
extern FILE* sqlin;
int sqlparse(void);


RC SqlEngine::run(FILE* commandline)
{
  fprintf(stdout, "Bruinbase> ");

  // set the command line input and start parsing user input
  sqlin = commandline;
  sqlparse();  // sqlparse() is defined in SqlParser.tab.c generated from
               // SqlParser.y by bison (bison is GNU equivalent of yacc)

  return 0;
}

/*
bool satisfyAllConds(IndexCursor& cursor, const vector<SelCond>& cond)
{	int key; 
	RecordId rid;
	RecordFile rf; 
	int    diff;
	string value;
	rf.open(table + ".tbl", 'r');  // make it pass by reference
	readForward(IndexCursor& cursor, int& key, RecordId& rid);
	
	
	 for (unsigned i = 0; i < cond.size(); i++) {
      // compute the difference between the tuple value and the condition value
      switch (cond[i].attr) {
      case 1:
	diff = key - atoi(cond[i].value);
	break;
      case 2:{ 
			    rf.read(rid, key, value);
				diff = strcmp(value.c_str(), cond[i].value);
			 }
	break;
	  }

      // skip the tuple if any condition is not met
      switch (cond[i].comp) {
      case SelCond::EQ:
	if (diff != 0) return false;
	;
      case SelCond::NE:
	if (diff == 0) return false;
	
      case SelCond::GT:
	if (diff <= 0) return false;
	
      case SelCond::LT:
	if (diff >= 0) return false;
	
      case SelCond::GE:
	if (diff < 0) return false;
	
      case SelCond::LE:
	if (diff > 0) return false;
	
        }
	 }  // close for loop
	
    // the condition is met for the tuple. 
    // increase matching tuple counter
   // count++;

    // print the tuple 
 /*   switch (attr) {
    case 1:  // SELECT key
      fprintf(stdout, "%d\n", key);
      break;
    case 2:  // SELECT value
      fprintf(stdout, "%s\n", value.c_str());
      break;
    case 3:  // SELECT *
      fprintf(stdout, "%d '%s'\n", key, value.c_str());
      break;
    }

    // move to the next tuple
    next_tuple:
    ++rid;
  }

  // print matching tuple count if "select count(*)"
 // if (attr == 4) {
//    fprintf(stdout, "%d\n", count);
//  }
//  rc = 0;

  // close the table file and return
// exit_select:
  //rf.close();
  return rc;
}

	 rf.close();
	 return true;
}
*/

RC SqlEngine::select(int attr, const string& table, const vector<SelCond>& cond)
{
  RecordFile rf;   // RecordFile containing the table
  RecordId   rid;  // record cursor for table scanning

  RC     rc;
  int    key;     
  string value;
  int    count=0;
  int    diff;
  
  // open the table file
  if ((rc = rf.open(table + ".tbl", 'r')) < 0) {
    fprintf(stderr, "Error: table %s does not exist\n", table.c_str());
    return rc;
  }

  BTreeIndex treeIdx;
  IndexCursor cursor;
  rc=treeIdx.open(table+".idx",'w');
  if(rc!=0)
	  cout<<"failed to open index "+table<<endl;
 // cout<<"after open, treeHeight is "<<treeIdx.treeHeight<<"rootPid is" <<treeIdx.rootPid<<endl;
//  cout<<"second print test "<<endl;
 // treeIdx.PrintTree();
  treeIdx.locate(treeIdx.smallestKey, cursor);
	//cout<<"smallestKey is  "<<treeIdx.smallestKey<<endl;
	while (treeIdx.readForward(cursor,  key,  rid)==0){
	//cout<<"have read key "<<key<<endl;
	bool satisfyAllCond=true;
	 for (unsigned i = 0; i < cond.size(); i++) {
      // compute the difference between the tuple value and the condition value
      switch (cond[i].attr) {
      case 1:
		diff = key - atoi(cond[i].value);
		break;
      case 2:{ 
			    rf.read(rid, key, value);
				diff = strcmp(value.c_str(), cond[i].value);
			 }
		break;
	  }

   

      // skip the tuple if any condition is not met
      switch (cond[i].comp) {
      case SelCond::EQ:
	if (diff != 0) satisfyAllCond=false;
	break;
      case SelCond::NE:
	if (diff == 0) satisfyAllCond=false;
	break;
      case SelCond::GT:
	if (diff <= 0) satisfyAllCond=false;
	break;
      case SelCond::LT:
	if (diff >= 0) satisfyAllCond=false;
	break;
      case SelCond::GE:
	if (diff < 0) satisfyAllCond=false;
	break;
      case SelCond::LE:
	if (diff > 0) satisfyAllCond=false;
	break;
       }
	}  // close for loop

    // the condition is met for the tuple. 
    // increase matching tuple counter
   if (satisfyAllCond){
	 count++;

    // print the tuple 
    switch (attr) {
    case 1:  // SELECT key
      fprintf(stdout, "%d\n", key);
      break;
    case 2:  // SELECT value
		 {rf.read(rid, key, value);
      fprintf(stdout, "%s\n", value.c_str());
      break;
		 }
    case 3:  // SELECT *
		{rf.read(rid, key, value);
      fprintf(stdout, "%d '%s'\n", key, value.c_str());
      break;
		}
	} //end switch

  
   }  //end if satisfy all cond
 } //end while
  // print matching tuple count if "select count(*)"
  if (attr == 4) {
    fprintf(stdout, "%d\n", count);
  }
  

  rc = 0;
// close the table file and return
 
  rf.close();
  treeIdx.close();
  return rc;
}

RC SqlEngine::load(const string& table, const string& loadfile, bool index)
{  
  /* your code here */
  RecordFile rf;   // RecordFile containing the table
  RecordId   rid; 
  RC     rc;
  BTreeIndex treeIdx;
  cout<<"inside load"<<endl;
  if (index){		
	rc=treeIdx.open(table+".idx",'w');
	if(rc!=0){ cout<<"open index failed when loading "<<endl;
	return RC_FILE_OPEN_FAILED; 
	}
  }
  treeIdx.clear();
  cout<<"we get here"<<endl;
	string line; string value; int key;
  if (rc=rf.open(table + ".tbl", 'w') < 0) {
	  //cout<<"we get here"<<endl;
	  cout<<"open talbe fails!!!!"<<endl;
    fprintf(stderr, "Error: table %s does not exist\n", table.c_str());
    return rc;
  }
    cout<<"open tbl is successful"<<endl;
  cout<<rc;
  ifstream  input(loadfile.c_str());
  if(!input.is_open())
  {return RC_FILE_READ_FAILED ;}
  
  while (true){
   getline(input,line);
   if (input.fail()) break;
   rc = parseLoadLine(line, key, value);  
   if (rc!=0)
	   return rc;
   rc =rf.append(key, value,rid);   
    if (rc!=0)
	   return rc;
	//cout<<"we get heere1 in sqlengine load"<<endl;
	if(index){ //cout<<"we get heere1 in sqlengine load"<<endl;
		if(rc=treeIdx.insert(key,rid)!=0)
			return rc;
	}
  }
  rf.close();
  //   treeIdx.PrintTree();
  if(index)
	  treeIdx.close();

   
   

  return 0;
}

RC SqlEngine::parseLoadLine(const string& line, int& key, string& value)
{
    const char *s;
    char        c;
    string::size_type loc;
    
    // ignore beginning white spaces
    c = *(s = line.c_str());
    while (c == ' ' || c == '\t') { c = *++s; }

    // get the integer key value
    key = atoi(s);

    // look for comma
    s = strchr(s, ',');
    if (s == NULL) { return RC_INVALID_FILE_FORMAT; }

    // ignore white spaces
    do { c = *++s; } while (c == ' ' || c == '\t');
    
    // if there is nothing left, set the value to empty string
    if (c == 0) { 
        value.erase();
        return 0;
    }

    // is the value field delimited by ' or "?
    if (c == '\'' || c == '"') {
        s++;
    } else {
        c = '\n';
    }

    // get the value string
    value.assign(s);
    loc = value.find(c, 0);
    if (loc != string::npos) { value.erase(loc); }

    return 0;
}
